package site.xuqing.autohelperengine.util;

import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.GestureDescription;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.accessibility.AccessibilityNodeInfo;

import java.util.ArrayList;
import java.util.List;

import site.xuqing.autohelper.bean.StepBean;
import site.xuqing.autohelper.bean.StepInfoBean;
import site.xuqing.autohelper.exception.NodeNotFindException;
import site.xuqing.autohelper.type.NodeType;
import site.xuqing.autohelper.type.ScrollType;
import site.xuqing.autohelper.util.StepExecutor;
import site.xuqing.autohelper.util.StringUtil;

/**
 * @author XuQing
 */
public class StepExecutorImpl implements StepExecutor {
    @Override
    public void singleTap(AccessibilityNodeInfo node) throws Exception {
        if (node != null) {
            AccessibilityNodeInfo clickNode = findClickNode(node);
            if (clickNode != null) {
                if (clickNode.performAction(AccessibilityNodeInfo.ACTION_CLICK)) {
                    return;
                }
            }
        }
        throw new Exception("单击失败");
    }

    @Override
    public void doubleTap(AccessibilityNodeInfo node) throws Exception {
        if (node != null) {
            AccessibilityNodeInfo clickNode = findClickNode(node);
            if (clickNode != null) {
                if (clickNode.performAction(AccessibilityNodeInfo.ACTION_CLICK) &&
                        clickNode.performAction(AccessibilityNodeInfo.ACTION_CLICK)) {
                    return;
                }
            }
        }
        throw new Exception("双击失败");
    }

    @Override
    public void longTap(AccessibilityNodeInfo node) throws Exception {
        if (node != null) {
            AccessibilityNodeInfo longClickNode = findLongClickNode(node);
            if (longClickNode != null) {
                if (longClickNode.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK)) {
                    return;
                }
            }
        }
        throw new Exception("长按失败");
    }

    @Override
    public void rectSingleTap(AccessibilityService service, AccessibilityNodeInfo node) throws Exception {
        Path p = new Path();
        Rect rect = new Rect();
        node.getBoundsInScreen(rect);
        p.moveTo((float) (rect.left + rect.right) / 2, (float) (rect.top + rect.bottom) / 2);
        doGesture(service, p, 100L);
    }

    @Override
    public void rectLongTap(AccessibilityService service, AccessibilityNodeInfo node) throws Exception {
        Path p = new Path();
        Rect rect = new Rect();
        node.getBoundsInScreen(rect);
        p.moveTo((float) (rect.left + rect.right) / 2, (float) (rect.top + rect.bottom) / 2);
        doGesture(service, p, 500L);
    }

    @Override
    public void back(AccessibilityService service) throws Exception {
        if (!service.performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK)) {
            throw new Exception("返回失败");
        }
    }

    @Override
    public AccessibilityNodeInfo findStepInfoNode(AccessibilityNodeInfo node, StepBean stepBean) throws Exception {
        return findStepInfoNode(node, stepBean.getStepInfo(), stepBean.getStepInfoParent(), stepBean.getStepInfoChild());
    }

    @Override
    public AccessibilityNodeInfo findStepInfoNode(AccessibilityNodeInfo node, StepInfoBean stepInfo, StepInfoBean stepInfoParent, StepInfoBean stepInfoChild) throws Exception {
        if (node != null) {
            if (stepInfo != null) {
                System.out.println("text=" + stepInfo.getText() + ";id=" + stepInfo.getId() + ";className=" + stepInfo.getClassName() + ";desc=" + stepInfo.getDesc());
                List<AccessibilityNodeInfo> nodeList = findStepInfoNodes(node, stepInfo);
                if (nodeList.size() <= 0) {
                    throw new NodeNotFindException("未查找节点，请重新选择能确保唯一性的节点，以确保能查找到节点");
                } else if (nodeList.size() == 1) {
                    return nodeList.get(0);
                } else {
                    boolean isEquals = true;
                    AccessibilityNodeInfo prev = null;
                    for (AccessibilityNodeInfo accessibilityNodeInfo : nodeList) {
                        if (prev == null) {
                            prev = accessibilityNodeInfo;
                        } else {
                            Rect prevRect = new Rect();
                            Rect currentRect = new Rect();
                            accessibilityNodeInfo.getBoundsInScreen(currentRect);
                            prev.getBoundsInScreen(prevRect);
                            if (prevRect.equals(currentRect)) {
                                prev = accessibilityNodeInfo;
                            } else {
                                isEquals = false;
                                break;
                            }
                        }
                    }
                    if (isEquals) {
                        return nodeList.get(0);
                    }
                    if (stepInfoParent == null && stepInfoChild == null) {
                        throw new Exception("节点不唯一，" + "查找到的节点个数为" + nodeList.size() + ",请重新选择能确保唯一性的节点");
                    } else {
                        //取交集
                        if (stepInfoParent != null) {
                            nodeList.retainAll(findStepInfoNodes(node, stepInfoParent));
                        }
                        if (stepInfoChild != null) {
                            nodeList.retainAll(findStepInfoNodes(node, stepInfoChild));
                        }
                        if (nodeList.size() <= 0) {
                            throw new Exception("未查找节点，请重新选择能确保唯一性的节点，以确保能查找到节点");
                        } else if (nodeList.size() == 1) {
                            return nodeList.get(0);
                        } else {
                            throw new Exception("节点不唯一，" + "查找到的节点个数为" + nodeList.size() + ",请重新选择能确保唯一性的节点");
                        }
                    }
                }
            } else {
                throw new Exception("数据错误，操作信息operateInfo不可为空");
            }
        }
        throw new Exception("系统错误，操作根节点不可为空");
    }

    /**
     * 根据StepInfoBean查找node
     *
     * @param node     根节点
     * @param stepInfo 操作对象
     * @return List<AccessibilityNodeInfo>
     */
    @Override
    public List<AccessibilityNodeInfo> findStepInfoNodes(AccessibilityNodeInfo node, StepInfoBean stepInfo) {
        if (node != null && stepInfo != null) {
            List<AccessibilityNodeInfo> nodeList = new ArrayList<>();
            //先取并集
            if (!StringUtil.isEmptyString(stepInfo.getText())) {
                nodeList.addAll(node.findAccessibilityNodeInfosByText(stepInfo.getText()));
            } else if (!StringUtil.isEmptyString(stepInfo.getDesc())) {
                List<AccessibilityNodeInfo> list = new ArrayList<>();
                findNodes(node, NodeType.DESC, stepInfo.getDesc(), list);
                nodeList.addAll(list);
            } else if (!StringUtil.isEmptyString(stepInfo.getId())) {
                nodeList.addAll(node.findAccessibilityNodeInfosByViewId(stepInfo.getId()));
            } else if (!StringUtil.isEmptyString(stepInfo.getClassName())) {
                List<AccessibilityNodeInfo> list = new ArrayList<>();
                findNodes(node, NodeType.CLASS_NAME, stepInfo.getClassName(), list);
                nodeList.addAll(list);
            }
            //再取交集
            if (!StringUtil.isEmptyString(stepInfo.getText())) {
                nodeList.retainAll(node.findAccessibilityNodeInfosByText(stepInfo.getText()));
            }
            if (!StringUtil.isEmptyString(stepInfo.getDesc())) {
                List<AccessibilityNodeInfo> list = new ArrayList<>();
                findNodes(node, NodeType.DESC, stepInfo.getDesc(), list);
                nodeList.retainAll(list);
            }
            if (!StringUtil.isEmptyString(stepInfo.getId())) {
                nodeList.retainAll(node.findAccessibilityNodeInfosByViewId(stepInfo.getId()));
            }
            if (!StringUtil.isEmptyString(stepInfo.getClassName())) {
                List<AccessibilityNodeInfo> list = new ArrayList<>();
                findNodes(node, NodeType.CLASS_NAME, stepInfo.getClassName(), list);
                nodeList.retainAll(list);
            }
            return nodeList;
        }
        return null;
    }

    /**
     * 递归查找节点
     *
     * @param node     根节点
     * @param nodeType 节点类别
     * @param target   要查找的节点的特征值，根绝节点类别而不同
     * @param result   查找到的节点
     */
    private static void findNodes(AccessibilityNodeInfo node, NodeType nodeType, String target, List<AccessibilityNodeInfo> result) {
        if (node == null) {
            return;
        }
        if (target.equals((nodeType == NodeType.DESC ? node.getContentDescription() + "" : (nodeType == NodeType.CLASS_NAME ? node.getClassName() + "" : "")))) {
            result.add(node);
        }
        for (int i = 0; i < node.getChildCount(); i++) {
            findNodes(node.getChild(i), nodeType, target, result);
        }
    }

    @Override
    public void doGesture(AccessibilityService service, Path p, Long duration) throws Exception {
        GestureDescription.Builder builder = new GestureDescription.Builder();
        builder.addStroke(new GestureDescription.StrokeDescription(p, 0L, duration));
        GestureDescription gesture = builder.build();
        boolean isDispatched = service.dispatchGesture(gesture, new AccessibilityService.GestureResultCallback() {
            @Override
            public void onCompleted(GestureDescription gestureDescription) {
                super.onCompleted(gestureDescription);
                System.out.println("doGesture onCompleted: 完成..........");
            }

            @Override
            public void onCancelled(GestureDescription gestureDescription) {
                super.onCancelled(gestureDescription);
                System.out.println("doGesture onCompleted: 取消..........");
            }
        }, null);
        System.out.println("doGesture模拟步骤执行：" + isDispatched);
        if (!isDispatched) {
            throw new Exception("doGesture模拟步骤（例如滑动）执行失败");
        }
    }

    @Override
    public void findNodesByClassName(AccessibilityNodeInfo node, String className, List<AccessibilityNodeInfo> result) {
        if (node == null) {
            return;
        }

        if (node.getClassName() != null && className.equals(node.getClassName().toString())) {
            result.add(node);
        }

        for (int i = 0; i < node.getChildCount(); i++) {
            findNodesByClassName(node.getChild(i), className, result);
        }
    }

    @Override
    public void findNodesByText(AccessibilityNodeInfo node, String text, List<AccessibilityNodeInfo> result) {
        if (node == null) {
            return;
        }

        if (node.getText() != null && text.equals(node.getText().toString())) {
            result.add(node);
        }

        for (int i = 0; i < node.getChildCount(); i++) {
            findNodesByText(node.getChild(i), text, result);
        }
    }

    @Override
    public void printNodes(AccessibilityNodeInfo node) {
        if (node == null) {
            return;
        }
        System.out.println("parentNodeHashCode:" + (node.getParent() == null ? "null" : node.getParent().hashCode()) + "---------className:" + node.getClassName() + "---------text:" + node.getText() + "---------desc:" + node.getContentDescription() + "---------id:" + node.getViewIdResourceName());
        for (int i = 0; i < node.getChildCount(); i++) {
            printNodes(node.getChild(i));
        }
    }

    @Override
    public void copy(AccessibilityService service, AccessibilityNodeInfo textNode) {
        String text = textNode.getText().toString();
        ClipboardManager clipboard = (ClipboardManager) service.getSystemService(Context.CLIPBOARD_SERVICE);
        ClipData clip = ClipData.newPlainText("text", text);
        clipboard.setPrimaryClip(clip);
    }

    @Override
    public void paste(AccessibilityNodeInfo editNode) throws Exception {
        if (editNode != null) {
            editNode.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
            boolean b = editNode.performAction(AccessibilityNodeInfo.ACTION_PASTE);
            if (!b) {
                throw new Exception("粘贴失败");
            }
        }
    }

    @Override
    public void paste(AccessibilityNodeInfo editNode, String pasteText) throws Exception {
        if (editNode != null) {
            if (!"".equals(pasteText)) {
                Bundle arguments = new Bundle();
                arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, pasteText);
                boolean b = editNode.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
                if (!b) {
                    throw new Exception("粘贴失败");
                }
            } else {
                throw new Exception("待粘贴的文本为空");
            }
        }
    }

    /**
     * 寻找顶层可点击节点
     *
     * @param child 子节点
     * @return AccessibilityNodeInfo
     */
    @Override
    public AccessibilityNodeInfo findClickNode(AccessibilityNodeInfo child) {
        if (child == null) {
            return null;
        }
        if (child.isClickable()) {
            return child;
        }
        if (child.getParent() == null) {
            return null;
        }
        return findClickNode(child.getParent());
    }

    /**
     * 寻找顶层可长按节点
     *
     * @param child 子节点
     * @return AccessibilityNodeInfo
     */
    @Override
    public AccessibilityNodeInfo findLongClickNode(AccessibilityNodeInfo child) {
        if (child == null) {
            return null;
        }
        if (child.isLongClickable()) {
            return child;
        }
        if (child.getParent() == null) {
            return null;
        }
        return findLongClickNode(child.getParent());
    }

    /**
     * 滑动事件
     *
     * @param direction 滑动方向
     */
    public void scroll(AccessibilityService service, ScrollType direction) throws Exception {
        Point start = new Point(), end = new Point();
        int offsetHeight = ScreenUtil.getInstance().screenSizeHeight / 4;
        int offsetWidth = ScreenUtil.getInstance().screenSizeWidth / 4;
        switch (direction) {
            case UP:
                start.x = ScreenUtil.getInstance().screenSizeWidth / 2;
                start.y = ScreenUtil.getInstance().screenSizeHeight - offsetHeight;
                end.x = ScreenUtil.getInstance().screenSizeWidth / 2;
                end.y = offsetHeight;
                break;
            case DOWN:
                end.x = ScreenUtil.getInstance().screenSizeWidth / 2;
                end.y = ScreenUtil.getInstance().screenSizeHeight - offsetHeight;
                start.x = ScreenUtil.getInstance().screenSizeWidth / 2;
                start.y = offsetHeight;
                break;
            case LEFT:
                start.x = ScreenUtil.getInstance().screenSizeWidth - offsetWidth;
                start.y = ScreenUtil.getInstance().screenSizeHeight / 2;
                end.x = offsetWidth;
                end.y = ScreenUtil.getInstance().screenSizeHeight / 2;
                break;
            case RIGHT:
                end.x = ScreenUtil.getInstance().screenSizeWidth - offsetWidth;
                end.y = ScreenUtil.getInstance().screenSizeHeight / 2;
                start.x = offsetWidth;
                start.y = ScreenUtil.getInstance().screenSizeHeight / 2;
                break;
            default:
        }
        Path p = new Path();
        p.moveTo(start.x, start.y);
        p.lineTo(end.x, end.y);
        doGesture(service, p, 500L);
    }
}
